#include "gkt_bit.h"
#include "gkt_crc8.h"

#define GKT_CRC8_C	1
#include "crc8_table.h"
#undef GKT_CRC8_C

uint8_t crc8_calc_in_reverse_true(uint8_t poly,
					uint8_t crc_init,
					const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = crc_init;
	uint8_t byte;
	uint32_t i, j;

	for (i = 0; i < length; i++) {
		gkt_bit_reverse_u8(data[i], byte);
		crc_value ^= byte;
		for (j = 0; j < 8; j++) {
			if (crc_value & 0x80)
				crc_value = (crc_value << 1) ^ poly;
			else
				crc_value = crc_value << 1;
		}
	}

	return crc_value;
}

uint8_t crc8_calc_in_reverse_false(uint8_t poly,
					uint8_t crc_init,
					const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = crc_init;
	uint32_t i, j;

	for (i = 0; i < length; i++) {
		crc_value ^= data[i];
		for (j = 0; j < 8; j++) {
			if (crc_value & 0x80)
				crc_value = (crc_value << 1) ^ poly;
			else
				crc_value = crc_value << 1;
		}
	}

	return crc_value;
}

uint8_t crc8_table_in_reverse_true(uint8_t *crc_table,
					uint8_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = crc_init;
	uint8_t byte;
	uint32_t i;

	for (i = 0; i < length; i++) {
		gkt_bit_reverse_u8(data[i], byte);
		crc_value = crc_table[crc_value ^ byte];
	}

	return crc_value;
}

uint8_t crc8_table_in_reverse_false(uint8_t *crc_table,
					uint8_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = crc_init;
	uint32_t i;

	for (i = 0; i < length; i++)
		crc_value = crc_table[crc_value ^ data[i]];

	return crc_value;
}

uint8_t gkt_crc8(const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = GKT_CRC8_INIT;

	if (data && length)
		crc_value = crc8_calc_in_reverse_false(
							GKT_CRC8_POLY, 
							crc_value, data, length);

	return crc_value;
}

#define GKT_CRC8_ITU_OUT_XOR	0x55
uint8_t gkt_crc8_itu(const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = GKT_CRC8_ITU_INIT;

	if (data && length) {
		crc_value = crc8_calc_in_reverse_false(
							GKT_CRC8_ITU_POLY, 
							crc_value, data, length);
		crc_value ^= GKT_CRC8_ITU_OUT_XOR;
	}

	return crc_value;
}

uint8_t gkt_crc8_rohc(const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = GKT_CRC8_ROHC_INIT;

	if (data && length) {
		crc_value = crc8_calc_in_reverse_true(
							GKT_CRC8_ROHC_POLY, 
							crc_value, data, length);
		gkt_bit_reverse_u8(crc_value, crc_value);
	}

	return crc_value;
}

uint8_t gkt_crc8_maxim(const uint8_t *data, uint32_t length)
{
	uint8_t crc_value = GKT_CRC8_MAXIM_INIT;

	if (data && length) {
		crc_value = crc8_calc_in_reverse_true(
							GKT_CRC8_MAXIM_POLY, 
							crc_value, data, length);
		gkt_bit_reverse_u8(crc_value, crc_value);
	}

	return crc_value;
}

